<template>
  <el-upload
    ref="upload"
    v-bind="$attrs"
    class="ls-file-upload"
    :class="{ 'is-disabled': disabled }"
    :disabled="disabled"
    :action="getUrl('action')"
    :headers="headers"
    :list-type="componentType"
    :show-file-list="showUploadFileList"
    :accept="accept"
    v-model:file-list="defaultList"
    :limit="limit"
    :on-exceed="handleExceed"
    :before-upload="beforeUpload"
    :on-success="onSuccess"
    :on-remove="onRemove"
    :on-preview="onPreview"
  >
    <!-- 头像上传 -->
    <template v-if="listType === 'avatar'">
      <!-- 有头像 -->
      <div v-if="value" class="avatar" :class="{ disabled }" @click.stop>
        <!-- 直接访问写法 -->
        <el-image class="avatar-img" :src="getUrl('viewUrl') + value" fit="cover">
          <template #error>
            <!-- <el-image :src="errorImg" class="avatar-img-error"></el-image> -->
            <nodata class="small-imageErr" :data-config="{ type: 'imageErr' }" />
          </template>
        </el-image>
        <!-- token验证写法 -->
        <!-- <ls-image class="avatar-img" :viewUrl="viewUrl" :token="token" :src="value" fit="cover">
          <template #error>
            <el-image :src="errorImg" class="avatar-img-error"></el-image>
          </template>
        </ls-image> -->
        <div class="avatar-mode">
          <el-icon
            class="avatar-view-icon"
            @click.stop="onPreview({ url: getUrl('viewUrl') + value })"
            ><ZoomIn
          /></el-icon>
          <el-icon class="avatar-delete-icon" @click.stop="onRemove"><Delete /></el-icon>
        </div>
      </div>
      <!-- 没有头像 -->
      <div class="avatar" v-else>
        <div class="avatar-img" v-if="disabled">
          <!-- <el-image :src="noImg"></el-image> -->
          <nodata class="small-nodata" :data-config="{ type: 'image' }" />
        </div>
        <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
      </div>
    </template>
    <!-- 多图片上传 ↓ -->
    <template v-else-if="listType === 'picture-card'">
      <el-icon><Plus /></el-icon>
    </template>
    <!-- 文件上传 ↓ -->
    <template v-else-if="!disabled">
      <el-button type="primary" plain>{{ buttonText }}</el-button>
    </template>
    <template #tip v-if="!['avatar', 'picture-card'].includes(listType) && tip">
      <div class="el-upload__tip">
        <span class="p-r-2">(</span>
        <span v-if="accept">只能上传{{ acceptTips(accept) }}文件,且</span>
        <span v-if="limit">最多上传{{ limit }}个文件,</span>
        <span v-if="limitSize && unit !== 'MB'"
          >单个文件大小不超过{{ +(limitSize / 1024).toFixed(2) }}KB</span
        ><span v-if="limitSize && unit === 'MB'"
          >单个文件大小不超过{{ +(limitSize / 1024 / 1024).toFixed(2) }}MB</span
        >
        <span class="p-l-2">)</span>
      </div>
    </template>
    <template #tip v-else-if="tipText">
      <div class="el-upload__tip" :style="tipStyle">
        {{ tipText }}
      </div>
    </template>
  </el-upload>
  <!-- 图片预览弹窗 ↓ -->
  <el-image
    v-if="picturePreview"
    ref="image"
    style="width: 0px; height: 0px"
    :src="picturePreview"
    :preview-src-list="[picturePreview]"
    :preview-teleported="true"
  >
  </el-image>
</template>
<script setup>
import '../style/style.scss'
import axios from 'axios'
import { ElMessage } from 'element-plus'
import { onMounted, ref, nextTick, computed, inject } from 'vue'
import { Plus, ZoomIn, Delete } from '@element-plus/icons-vue'
import nodata from 'ls-nodata/src/index.vue'
import noImg from '../images/image_none.png'
import errorImg from '../images/image_error.png'
const injAction = inject('action')
const injViewUrl = inject('viewUrl')
const injDownloadUrl = inject('downloadUrl')
const props = defineProps({
  modelValue: [Array, String],
  listType: {
    type: String,
    default: 'text'
  },
  fileIdProp: {
    type: String,
    default: 'id'
  },
  fileNameProp: {
    type: String,
    default: 'name'
  },
  action: {
    type: String,
    default: '/rf/file/api/file/uploadFile'
  },
  viewUrl: {
    type: String,
    default: '/rf/file/api/file/view/'
  },
  downloadUrl: {
    type: String,
    default: '/rf/file/api/file/downloadFile/'
  },
  limit: {
    type: Number,
    default: 5
  },
  limitSize: {
    type: Number,
    default: 100 * 1024
  },
  unit: {
    type: String,
    default: 'kb'
  },
  limitNameLength: {
    type: Number,
    default: 100
  },
  accept: {
    type: String,
    default: ''
  },
  showFileList: {
    type: Boolean,
    default: true
  },
  disabled: Boolean,
  tip: {
    type: Boolean,
    default: true
  },
  buttonText: {
    type: String,
    default: '添加附件'
  },
  tipText: {
    type: String,
    default: ''
  },
  tipStyle: {
    type: String,
    default: '1'
  },
  token: {
    type: String,
    default: sessionStorage.getItem('TOKEN')
  },
  previewTeleported: {
    type: Boolean,
    default: false
  }
})
const emits = defineEmits(['fail', 'input', 'handlerSuccess', 'handlerRemove', 'update:modelValue'])
// 计算属性
const showUploadFileList = computed(() => {
  return props.listType !== 'avatar' ? props.showFileList : false
})
let value = computed({
  get() {
    return props.modelValue
  },
  set(val) {
    emits('update:modelValue', val)
  }
})
let componentType = computed(() => {
  return props.listType === 'avatar' ? 'picture' : props.listType
})
let defaultList = ref()
const image = ref(null)
const upload = ref(null)
const headers = {
  Authorization: sessionStorage.getItem('TOKEN')
}
const picturePreview = ref('')
// 方法
const getUrl = (name) => {
  switch (name) {
    case 'action':
      return injAction || props.action
    case 'viewUrl':
      return injViewUrl || props.viewUrl
    case 'downloadUrl':
      return injDownloadUrl || props.downloadUrl
  }
}
const acceptTips = (str) => {
  let majuscule = str.toLowerCase()
  if (majuscule.search(/\/[^\*]+/) > -1) {
    majuscule = majuscule.replace(/.+\//, '')
  }
  let list = majuscule.match(/[a-z0-9]+/g) || []
  list = Array.from(new Set(list))
  return list.join('、')
}
const beforeUpload = (file) => {
  let isType = true
  if (props.accept.includes('/')) {
    const fileTypeArr = file.type.split('/')
    const acceptTypeArr = props.accept.split('/')
    if (fileTypeArr[0] === acceptTypeArr[0]) {
      if (acceptTypeArr[1] !== '*') {
        isType = fileTypeArr[1] === acceptTypeArr[1]
      }
    } else {
      isType = false
    }
  } else if (props.accept) {
    const fileSuffix = file.name.replace(/.+\./, '')
    isType = props.accept.toLowerCase().includes(fileSuffix.toLowerCase())
  }
  const isSize = file.size <= props.limitSize
  const isName = file.name.length <= props.limitNameLength
  if (isType && isSize && isName) {
    return true
  } else {
    if (!isType) {
      ElMessage.error('上传格式不正确!')
    } else if (!isSize) {
      if (props.unit === 'MB') {
        ElMessage.error(`大小不能超过 ${+(props.limitSize / 1024 / 1024).toFixed(2)}MB!`)
      } else {
        ElMessage.error(`大小不能超过 ${+(props.limitSize / 1024).toFixed(2)}KB!`)
      }
    } else if (!isName) {
      ElMessage.error(`文件名长度不能超过 ${props.limitNameLength}个字符!`)
    }
    emits('fail', file)
    return false
  }
}
const handleExceed = () => {
  ElMessage.warning(`最多上传 ${props.limit} 个文件`)
}
const onPreview = (file) => {
  if (['picture-card', 'avatar'].includes(props.listType)) {
    // axios({
    //   method: 'get',
    //   url: file.url,
    //   responseType: 'blob',
    //   headers: { Authorization: props.token }
    // }).then((res) => {
    //   picturePreview.value = URL.createObjectURL(res.data)
    //   nextTick(() => {
    //     image.value.$el.children[0].click()
    //   })
    // })
    picturePreview.value = file.url
    nextTick(() => {
      image.value.$el.children[0].click()
    })
  } else if (file.id || file.response.code === 200) {
    axios
      .get(getUrl('downloadUrl') + (file.id || file.response.data.id), {
        headers: headers,
        responseType: 'blob'
      })
      .then((res) => {
        if (res.data.type === 'application/json') {
          const reader = new FileReader()
          reader.readAsText(res.data, 'utf-8')
          reader.onload = () => {
            const err = JSON.parse(reader.result)
            ElMessage.error(err.msg)
          }
        } else {
          let bl = res.data
          let fileName = window.decodeURI(res.headers['content-disposition'].split('=')[1], 'UTF-8')
          const link = document.createElement('a')
          link.href = window.URL.createObjectURL(bl)
          link.target = '_blank'
          link.download = fileName
          document.body.appendChild(link)
          link.click()
          document.body.removeChild(link)
          window.URL.revokeObjectURL(bl) // 释放掉blob对象
        }
      })
  }
}
const onSuccess = (res, file, fileList) => {
  if (res.code === 200) {
    if (props.listType === 'avatar') {
      emits('update:modelValue', res.data.id)
    } else {
      if (Array.isArray(value.value)) {
        const arr = value.value.concat({
          [props.fileNameProp]: res.data.name,
          [props.fileIdProp]: res.data.id
        })
        emits('update:modelValue', arr)
      } else {
        emits('update:modelValue', [
          {
            [props.fileNameProp]: res.data.name,
            [props.fileIdProp]: res.data.id
          }
        ])
      }
      renderFileIcon()
    }
  }
  emits('handlerSuccess', res, file, fileList)
}
const onRemove = (file, fileList) => {
  if (props.listType === 'avatar') {
    upload.value.clearFiles()
    emits('update:modelValue', '')
    emits('handlerRemove', value.value)
  } else {
    let files = []
    const id = file.response?.data.id || file.id
    if (Array.isArray(value.value)) {
      files = value.value.filter((v) => v[props.fileIdProp] !== id)
    }

    emits('update:modelValue', files)
    emits('handlerRemove', file, fileList)
  }
}
const renderFileIcon = () => {
  // 找出所有文件图标的class
  nextTick(() => {
    let fileElementList = document.getElementsByClassName('el-upload-list__item-name')
    if (fileElementList && fileElementList.length > 0) {
      for (let ele of fileElementList) {
        let fileName = ele.innerText
        //获取文件名后缀
        let fileType = fileName.substring(fileName.lastIndexOf('.') + 1)
        let iconElement = ele.getElementsByTagName('i')[0]
        iconElement.innerHTML = ''
        if (['png', 'jpg', 'jpeg', 'gif', 'PNG', 'JPG', 'JPEG', 'GIF'].indexOf(fileType) != -1) {
          iconElement.className = 'icon-image' // 图⽚，动图
        } else if (
          ['mp4', '3gp', 'avi', 'flv', 'MP4', '3GP', 'AVI', 'FLV'].indexOf(fileType) != -1
        ) {
          iconElement.className = 'icon-video' // 视频
        } else if (['doc', 'docx', 'DOC', 'DOCX'].indexOf(fileType) != -1) {
          iconElement.className = 'icon-word' // 文档
        } else if (['xls', 'xlsx', 'XLS', 'XLSX'].indexOf(fileType) != -1) {
          iconElement.className = 'icon-excel' // 表格
        } else if (['ppt', 'pptx', 'PPT', 'PPTX'].indexOf(fileType) != -1) {
          iconElement.className = 'icon-ppt' // PPT
        } else if (['pdf', 'PDF'].indexOf(fileType) != -1) {
          iconElement.className = 'icon-pdf' // PDF
        } else if (['zip', 'rar', '7-zip', 'tar', 'gzip', 'bzip2', 'xz'].indexOf(fileType) != -1) {
          iconElement.className = 'icon-zip' // PDF
        } else {
          iconElement.className = 'icon-default' //默认图标
        }
      }
    }
  })
}
const submit = () => {
  upload.value.submit()
}
// 初始化
onMounted(() => {
  if (Array.isArray(value.value)) {
    defaultList.value = value.value.map((v) => ({
      id: v[props.fileIdProp], // 下载需要
      name: v[props.fileNameProp], // 展示文件列表需要
      url: getUrl('viewUrl') + v[props.fileIdProp] // 展示图片列表需要
    }))
  }
  renderFileIcon()
})
defineExpose({
  submit,
  upload
})
</script>
<style lang="scss" scoped>
.small-nodata {
  top: 50%;
  :deep(img) {
    width: auto;
    height: 60px;
  }
  :deep(.no-data-tip) {
    font-size: 12px;
    position: relative;
    width: 100%;
  }
  :deep(.no-data-content) {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
  }
}
.small-imageErr {
  :deep(.no-data-tip) {
    font-size: 12px;
  }
}
</style>
